package boardgamer.yavalath.client.game;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.util.Pair;
import android.view.MotionEvent;
import boardgamer.common.client.AbstractView;
import boardgamer.common.client.Logger;
import boardgamer.common.client.animation.AnimationCollection;
import boardgamer.common.client.animation.WaitAnimation;
import boardgamer.common.client.layout.LayoutGroup;
import boardgamer.common.net.PlayerId;
import boardgamer.yavalath.client.RenderHelper;
import boardgamer.yavalath.client.animation.FadingBoardAnimation;
import boardgamer.yavalath.client.animation.HeaderAnimation;
import boardgamer.yavalath.client.animation.LoseAnimation;
import boardgamer.yavalath.client.animation.MoveAnimation;
import boardgamer.yavalath.client.animation.SelectedAnimation;
import boardgamer.yavalath.client.animation.SuggestionAnimation;
import boardgamer.yavalath.client.resources.SoundHandler;
import boardgamer.yavalath.client.resources.StringHandler;
import boardgamer.yavalath.common.YavalathBoard;
import boardgamer.yavalath.common.YavalathPlayerState;
import boardgamer.yavalath.common.YavalathTile;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

import java.util.Map;

import static boardgamer.yavalath.common.YavalathPlayerState.PlayerColor;

/**
 * @author garysoed
 */
public class GameView extends AbstractView<GameActivity> {

  public static final float HEADER_SCREEN_WIDTH_RATIO = 0.9f;
  public static final float HEADER_SCREEN_HEIGHT_RATIO = 0.1f;
  public static final float HEADER_GAP_SCREEN_HEIGHT_RATIO = 0.05f;

  private final AnimationCollection animations;
  private HeaderAnimation headerAnimation;
  private SuggestionAnimation suggestionAnimation;
  private SelectedAnimation selectedAnimation;
  private YavalathDrawableBoard board;
  private YavalathBoardLayout boardLayout;
  private Rect canvasRect;

  public GameView(GameActivity activity) {
    super(activity);
    this.animations = new AnimationCollection();
    this.suggestionAnimation = new SuggestionAnimation();

    this.suggestionAnimation = new SuggestionAnimation();
    this.boardLayout = new YavalathBoardLayout();
  }

  void addPlayerMove(PlayerId playerId, int tileX, int tileY) {
    board.getTile(tileX, tileY).setOwner(playerId);
    animations.addTemporary(new MoveAnimation(board, tileX, tileY));
    SoundHandler.playWave(SoundHandler.moveSound);
  }

  // TODO: Add playerId -> player color mapping
  void displayNextPlayer(String name, PlayerColor nextColor) {
    // TODO: move to strings.xml
    headerAnimation.display(name + "'s turn", nextColor);
  }

  @Override
  protected void handleDraw(Canvas canvas) {
    if (board != null) {
      board.draw(canvas);
    }
    animations.draw(canvas);
  }

  @Override
  protected void handleUpdate(int elapsedTime) {
    animations.update(elapsedTime);
  }

  @Override
  protected void handleLayout(Rect canvasRect) {
    this.canvasRect = canvasRect;
  }

  @Override
  protected void handleTouchEvent(MotionEvent event) {
    int eventAction = event.getAction();
    PointF touch = new PointF(event.getX(), event.getY());

    boolean animationBlocking = animations.isBlocking();
    boolean canSelectTile = activity.canSelectTile();
    boolean eventUp = eventAction == MotionEvent.ACTION_UP;

    if (!animationBlocking
        && eventUp
        && canSelectTile) {
      YavalathDrawableTile selectedTile = boardLayout.getTile(touch);
      activity.onTileSelected(selectedTile);
    }
  }

  public void displayPlayerLoss(final ImmutableSet<Pair<Integer, Integer>> losingTiles,
                                final PlayerId loser, final PlayerColor loserColor) {
    animations
        .addBlocking(FadingBoardAnimation.forDisappearingBoard(
            new Predicate<YavalathTile>() {
              @Override
              public boolean apply(YavalathTile input) {
                boolean isLosingTile
                    = losingTiles.contains(Pair.create(input.getX(), input.getY()));
                return !isLosingTile && input.hasOwner() && input.getOwner() == loser;
              }
            }, // notFadingPredicate
            board))
        .addBlocking(new WaitAnimation(1000),
            new AnimationCollection.DeactivateHandler() {
              @Override
              public void onDeactivate() {
                board.addLoser(loser);
                animations.addTemporary(new LoseAnimation(losingTiles, loserColor, board));
              }
            });
  }

  public void displayPlayerWin(final PlayerId winner, PlayerColor winnerColor) {
    animations
        .addBlocking(FadingBoardAnimation.forDisappearingBoard(
            new Predicate<YavalathTile>() {
              @Override
              public boolean apply(YavalathTile input) {
                return !input.hasOwner() || input.getOwner() != winner;
              }
            }, // fadingPredicate
            board),
            new AnimationCollection.DeactivateHandler() {
              @Override
              public void onDeactivate() {
                activity.showGameOver(
                    StringHandler.instance().getWinMessageDialog(winner.getName()));
              }
            });
    headerAnimation.display(StringHandler.instance().getWinMessage(winner.getName()), winnerColor);
  }

  public void displayStartGame(Map<PlayerId, YavalathPlayerState> playerStates,
      YavalathBoard startingBoard, final String firstPlayerName, final PlayerColor firstPlayerColor) {
    animations.clear();
    animations.addTemporary(suggestionAnimation);

    this.board = new YavalathDrawableBoard(
        new YavalathDrawableTile.Factory(playerStates, 0 /* startAlpha */),
        startingBoard);
    boardLayout.setBoard(this.board);

    // Fit the board to the screen
    new LayoutGroup()
        .add(boardLayout).atCurrentRect()
        .setBoundingRect(new RectF(canvasRect));

    headerAnimation = new HeaderAnimation(
        canvasRect.width() * HEADER_SCREEN_WIDTH_RATIO,
        canvasRect.height() * HEADER_SCREEN_HEIGHT_RATIO);

    selectedAnimation = new SelectedAnimation(canvasRect);
    selectedAnimation.init(playerStates);
    animations.addTemporary(selectedAnimation);

    // Layout the board and the message
    new LayoutGroup()
        .add(boardLayout).atCurrentRect()
        .add(headerAnimation).relativeToEdge(
        boardLayout,
        LayoutGroup.Direction.BOTTOM,
        canvasRect.height() * HEADER_GAP_SCREEN_HEIGHT_RATIO)
        .horizontalAlign(LayoutGroup.HorizontalAlign.CENTER)
        .setBoundingRect(new RectF(canvasRect));

    animations.addBlocking(FadingBoardAnimation.forAppearingBoard(this.board),
        new AnimationCollection.DeactivateHandler() {
          @Override
          public void onDeactivate() {
            displayNextPlayer(firstPlayerName, firstPlayerColor);
          }
        });
    animations.addTemporary(headerAnimation);
  }

  public void displayDraw() {
    activity.showGameOver(StringHandler.instance().gameDrawDialog);
    headerAnimation.display(StringHandler.instance().gameDrawMessage, Color.WHITE);
  }

  public void setBoard(YavalathBoard newBoard) {
    if (board != null) {
      board.update(newBoard);
    }
  }

  public void addSuggestionTiles(ImmutableSet<YavalathTile> suggestedTiles, PlayerColor playerColor) {
    Iterable<YavalathDrawableTile> winningTiles = Iterables.transform(
        suggestedTiles,
        new Function<YavalathTile, YavalathDrawableTile>() {

          @Override
          public YavalathDrawableTile apply(YavalathTile input) {
            return board.getTile(input.getX(), input.getY());
          }
        });
    suggestionAnimation.setTiles(
        RenderHelper.instance().getColor(playerColor),
        ImmutableSet.copyOf(winningTiles));
  }

  public void clearSuggestionTiles() {
    suggestionAnimation.setTiles(Color.BLACK, ImmutableSet.<YavalathDrawableTile>of());
  }

  public void selectTile(YavalathDrawableTile selectedTile, PlayerId currentPlayer) {
    selectedAnimation.setSelected(selectedTile, currentPlayer);
  }

  public void clearSelection() {
    selectedAnimation.turnOff();
  }
}
